feat: fix llm vidibility bug fix, article body appearence in server sent html#382
feat: fix llm vidibility bug fix, article body appearence in server sent html#382
Conversation
Signed-off-by: amaan-bhati <amaanbhati49@gmail.com>
There was a problem hiding this comment.
Pull request overview
This PR aims to make blog post article bodies visible in server-rendered HTML so non-JS crawlers (e.g., GPTBot/ClaudeBot) can read full post content, by enabling SSR for PostBody and making its HTML entity decoding SSR-safe.
Changes:
- Made
decodeHtmlEntitiesincomponents/post-body.tsxsafe to run during SSR by guardingdocumentusage and adding a string-replace fallback. - Removed
ssr: falsefrom the dynamicPostBodyimports in the technology and community post pages to allow server rendering. - Added an E2E regression test intended to verify post body content exists in the raw SSR HTML response.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| components/post-body.tsx | Adds SSR-safe HTML entity decoding to unblock server rendering. |
| pages/technology/[slug].tsx | Enables SSR for PostBody by removing ssr: false from the dynamic import. |
| pages/community/[slug].tsx | Enables SSR for PostBody by removing ssr: false from the dynamic import. |
| tests/e2e/SeoMeta.spec.ts | Adds a regression test intended to validate SSR HTML contains post prose before JS executes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Signed-off-by: amaan-bhati <amaanbhati49@gmail.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Signed-off-by: amaan-bhati <amaanbhati49@gmail.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
LLM Bot Visibility Fix - Blog Post Body SSR
The Bug
GPTBot, ClaudeBot, and PerplexityBot could not read Keploy blog post content.
When these crawlers fetched a post page, the
<article>body contained only thetitle, author, and a
"No posts found matching ''"placeholder — zero prose.Googlebot was unaffected because it executes JavaScript (since 2019) and waits
for hydration. LLM bots do not execute JavaScript; they parse the raw HTML
response and move on.
Root Cause Analysis
The
PostBodycomponent - which renders the entire article body — was importedwith
ssr: falsein both post page files:next/dynamicwithssr: falsetells Next.js to skip server-side rendering forthat component entirely. The article content existed in the
__NEXT_DATA__JSONblob (so Googlebot could hydrate it), but was never written into the HTML DOM
before JavaScript ran.
ssr: falsewas originally added becausepost-body.tsxcalleddocument.createElement("textarea")synchronously insiderenderCodeBlocks()—a browser-only API that would crash the Next.js build for any post containing
code blocks.
Fix
1. Make
decodeHtmlEntitiesSSR-safe -components/post-body.tsxReplaced the bare
document.createElementcall with atypeof documentguard plus a pure-JS fallback that covers all HTML entities WordPress generates:
2. Remove
ssr: falsefrom PostBody importspages/technology/[slug].tsxandpages/community/[slug].tsx:dynamic()is kept so the heavy PostBody bundle is still code-split. Only thessr: falseflag is removed, unlocking server rendering.Files Changed
components/post-body.tsxdecodeHtmlEntitiesguarded for SSR; pure-JS fallback addedpages/technology/[slug].tsxssr: falseremoved from PostBody dynamic importpages/community/[slug].tsxssr: falseremoved from PostBody dynamic importtests/e2e/SeoMeta.spec.tsWhy Nothing Else Breaks
Every other browser-only API in
PostBodyis already safe:window/documentcalls (resize listener,IntersectionObserver, hashscroll, history) live inside
useEffect— not called during SSR.CodeMirror,AuthorCard,BlogSidebar,JsonDiffViewereach retain theirown
ssr: false— they render asnullon the server and hydrate on theclient. Code blocks appear as empty containers in SSR HTML; the prose around
them is fully visible.
TOCcomponent usescreatePortal(…, document.body)inside a{show && …}gate whereshowstarts asfalse—document.bodyis never touchedduring SSR.
useState(content || "")gives SSR and the initial client render identicaloutput.
suppressHydrationWarning(already present on both content<div>s)covers the minor diff after the post-mount
useEffecttransforms the content.Testing
Existing coverage (unchanged, all pass)
tests/components/PostBody.spec.ts- navigates to a real post in a browser,asserts
[data-testid="post-content"]is visible, checks text length > 50characters, code blocks, copy buttons, TOC, author card.
tests/pages/TechnologyPostPage.spec.ts/CommunityPostPage.spec.ts—click through to a post and assert PostBody renders.
CSR behaviour. Zero breakage.
npm run buildandnpm run start, nothing breaksNew regression test -
tests/e2e/SeoMeta.spec.tsThis test uses Playwright's
requestAPI — an HTTP client with no browser andno JavaScript execution — exactly replicating how GPTBot and ClaudeBot crawl a
page. It would have failed before this fix (prose only existed in the
stripped JSON blob) and now passes (prose is present in the SSR HTML).
TypeScript reports zero errors across the entire project after all changes.